package nachos.threads;

import nachos.machine.*;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>,
 * and multiple threads can be waiting to <i>listen</i>. But there should never
 * be a time when both a speaker and a listener are waiting, because the two
 * threads can be paired off at this point.
 */
public class Communicator {
    /**
     * Allocate a new communicator.
     */
    


    private Lock conditionLock = new Lock();
    private Condition2 speakerCondition = new Condition2(this.conditionLock);
    private Condition2 listenerCondition =new Condition2(this.conditionLock);
    private Condition2 waitingSpeakerCondition = new Condition2(this.conditionLock);
    private int listeners;
    

    private boolean speakerThread;

    private int wordToListen;

    public Communicator() {
        

    }

    /**
     * Wait for a thread to listen through this communicator, and then transfer
     * <i>word</i> to the listener.
     *
     * <p>
     * Does not return until this thread is paired up with a listening thread.
     * Exactly one listener should receive <i>word</i>.
     *
     * @param	word	the integer to transfer.
     */
    public void speak(int word) {

        this.conditionLock.acquire();
        while(listeners == 0 || this.speakerThread != false)
            this.speakerCondition.sleep();
        
        this.wordToListen = word;
        this.speakerThread = true;
        this.listenerCondition.wake();
        this.waitingSpeakerCondition.sleep();
        this.conditionLock.release();
        return;
    }

    /**
     * Wait for a thread to speak through this communicator, and then return
     * the <i>word</i> that thread passed to <tt>speak()</tt>.
     *
     * @return	the integer transferred.
     */    
    public int listen(){
        int word = 0;

        this.conditionLock.acquire();
        listeners++;
        this.speakerCondition.wake();
        while(speakerThread == false)
            this.listenerCondition.sleep();
        word = this.wordToListen;
        
        waitingSpeakerCondition.wakeAll();

        speakerThread = false;
        this.speakerCondition.wake();
        listeners--;
        this.conditionLock.release();

	return word;
    }
}
